/*
 *  $Id: TilesWindow.java 4 2008-11-21 20:24:10Z Nathaniel.Waisbrot $
    Copyright (C) 2008  Nathaniel Waisbrot

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package termint.gui.window;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import termint.brains.MessageWatcher;
import termint.gui.GUIUtil;
import termint.gui.SWTImageBasic;
import termint.gui.vt.VT320;
import termint.gui.vt.VTElement;

/**
 * 
 * TilesWindow is the GUI object for displaying tiles.  It handles creating, resizing, and scrolling
 * the window.
 *
 * @see SWTImageBasic
 * @author Nathaniel Waisbrot
 */
public class TilesWindow extends Window {
	// basic window stuff
	private SWTImageBasic tiles;
	private ScrolledComposite scroller;

	// contextual menu stuff
	private final Menu tileMenu;
	private MenuItem[] tileMenuItems;
	
	private VTElement lastTile;
	private Point lastMenuLoc;
	
	private final MessageScrollback messagesWindow;
	
	/** space between the edge of the window and the window's content */
	private static final int windowMargins = 10;

	/**
	 * Create, but do not open, a new window to display a tiles map
	 * @param display The display to draw on
	 * @param vt The VT320 which backs the display
	 */
	public TilesWindow(Shell parent, VT320 vt) {
		super(parent.getDisplay());
		
		window.setLayout(new FillLayout());
		window.setText("Tiles");
		
		scroller = new ScrolledComposite(window, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
		tiles = new SWTImageBasic(scroller, vt);
		scroller.setContent(tiles);
		
		// MENU SETUP
		tileMenu = new Menu(window);
		tileMenuItems = new MenuItem[3];
		tileMenuItems[0] = new MenuItem(tileMenu, SWT.CASCADE);
		tileMenuItems[0].setText("title");
		tileMenuItems[0].setEnabled(false);
		tileMenuItems[1] = new MenuItem(tileMenu, SWT.SEPARATOR);
		tileMenuItems[2] = new MenuItem(tileMenu, SWT.CASCADE);
		tileMenuItems[2].setText("Change tile");
		
		tiles.addListener(SWT.MouseUp, new Listener() {
			public void handleEvent(Event e) {
				// if it's the right-button, pop up the menu
				if (e.button == 3) {
					lastTile = tiles.tileAtPoint(e.x, e.y);
					tileMenuItems[0].setText(lastTile.toString());
					lastMenuLoc = GUIUtil.screenPointFromControlPoint(tiles, e.x, e.y);
					tileMenu.setLocation(lastMenuLoc);
					tileMenu.setVisible(true);
				}}});
		
		tileMenuItems[2].addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				if (e.widget.equals(tileMenuItems[2])) {
					TileChanger dbox = new TileChanger(window, lastTile.toString(), lastMenuLoc);
					Integer idx = dbox.open();
					if (idx != null) {
						tiles.setTileByIndex(lastTile, idx);
						tiles.redraw();
						tiles.saveTiles();
					}}}});
		
		this.messagesWindow = new MessageScrollback(this, windowMargins);
	}
	
	public void setMessageWatcher(MessageWatcher mw) {
		mw.addMessageListener(messagesWindow);
	}
	
	/**
	 * Display the window
	 */
	public void open() {
		window.setBounds(getBounds());
		super.open();
		messagesWindow.open();
	}
	
	/**
	 * Calculate the bounds for this window -- it should occupy as much of the screen as it needs to
	 * to display its contents.
	 * @return Rectangle representing the bounds
	 */
	public Rectangle getBounds() {
		Rectangle maxBounds = getDisplay().getClientArea();
		Point mapSize = tiles.getSize();
		Rectangle untrimmed = scroller.computeTrim(0, 0, mapSize.x, mapSize.y);
		return new Rectangle(maxBounds.x, maxBounds.y,
				Math.min(untrimmed.width, maxBounds.width), Math.min(untrimmed.height, maxBounds.height));
	}
	
	/**
	 * Ask the window to redraw its contents
	 */
	public void redraw() {
		tiles.redraw();
	}

	/**
	 * Add a KeyListener to listen for keyboard events on this window
	 * @param l the KeyListener to add
	 */
	public void addKeyListener(KeyListener l) {
		tiles.addKeyListener(l);
	}
	
	public void addListener(int eventType, Listener l) {
		tiles.addListener(eventType, l);		
	}

	
	/**
	 * Remove SWT objects that use system memory
	 */
	public void dispose() {
		tiles.dispose();
		scroller.dispose();
		messagesWindow.dispose();
		super.dispose();
	}
	
	@Override
	public void setVisible(boolean b) {
		super.setVisible(b);
		messagesWindow.setVisible(b);
	}

	private static class TileChanger extends Dialog {
		private Shell dbox;
		private Text field;
		private Integer result;
		
		private Shell parent;
		private String tileDescription;
		private Point lastMenuLoc;
		
		private static final int dlogW = 400;
		private static final int dlogH = 150;
		
		public TileChanger(Shell parent, String tileDescription, Point lastMenuLoc) {
			super(parent, SWT.NONE);
			this.parent = parent;
			this.tileDescription = tileDescription;
			this.lastMenuLoc = lastMenuLoc;
			result = null;
		}
		
		public Integer open() {
			dbox = new Shell(parent, SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL);
			

			GridLayout layout = new GridLayout(3, false);
			layout.verticalSpacing = 8;
			layout.marginLeft = 4;
			layout.marginRight = 4;
			dbox.setLayout(layout);
			dbox.setText("Set new tile");
			
			Label prompt = new Label(dbox, SWT.NORMAL);
			prompt.setText("Choose an index number for this tile ("+tileDescription+")");
			GridData promptGD = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
			promptGD.horizontalSpan = 3;
			prompt.setLayoutData(promptGD);
			
			field = new Text(dbox, SWT.SINGLE | SWT.BORDER);
			field.setTextLimit(50);
			GridData fieldGD = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
			promptGD.horizontalSpan = 3;
			field.setLayoutData(fieldGD);
			field.addVerifyListener(new VerifyListener() {
				public void verifyText(VerifyEvent e) {
					try {
						e.doit = (Integer.parseInt(e.text) >= 0);
					} catch (NumberFormatException x) {
						e.doit = false;
					}}});
			field.addListener(SWT.DefaultSelection, new Listener() {
				public void handleEvent(Event e) {
					result = parseTextInput(field.getText());
					dbox.dispose();
				}});
			
			Label filler = new Label(dbox, SWT.NULL);
			GridData fillerGD = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
			fillerGD.horizontalSpan = 2;
			filler.setLayoutData(fillerGD);
			
			Button cancel = new Button(dbox, SWT.PUSH);
			GridData cancelGD = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
			cancelGD.horizontalSpan = 1;
			cancel.setLayoutData(cancelGD);
			cancel.setText("Cancel");
			cancel.addListener(SWT.Selection, new Listener() {
				public void handleEvent(Event e) {
					dbox.dispose();
				}});

			Button ok = new Button(dbox, SWT.PUSH);
			ok.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_BLUE));
			GridData okGD = new GridData(GridData.HORIZONTAL_ALIGN_END);
			okGD.horizontalSpan = 1;
			ok.setLayoutData(okGD);
			ok.setText("OK");
			ok.addListener(SWT.Selection, new Listener() {
				public void handleEvent(Event e) {
					result = parseTextInput(field.getText());
					dbox.dispose();
				}});
			
			dbox.setBounds(lastMenuLoc.x, lastMenuLoc.y, dlogW, dlogH);
			dbox.pack();
			dbox.open();
			
			while (!dbox.isDisposed()) {
				if (!getParent().getDisplay().readAndDispatch()) {
					getParent().getDisplay().sleep();
				}
			}
			
			return result;
		}
		
		private Integer parseTextInput(String s) {
			Integer i = null;
			try {
				String res = field.getText();
				if (res != null && res.length() > 0)
					i = Integer.parseInt(res);
			} catch (NumberFormatException e) {
				// it's OK
			}
			return i;
		}
	}
}
